ウェブサイト構築 ~コンテンツ管理~

最終更新: 2025年8月29日 14:06

ウェブサイト構築 コンテンツ管理

対象読者

ウェブサイトで記事を公開しようとする人。開発者、コンテンツの作成・管理者。

この記事で扱う範囲・扱わない範囲

スクラッチでウェブサイトを構築する場合を想定し、コンテンツ管理周りについて記載する。 Wordpressやその他ノーコードツールなどでの構築の場合には一部適さない内容もある。

目的

ウェブサイトの訪問者が、目的の情報までたどり着きやすく、見やすく、読みやすくしながらも、運用する側も情報を届けやすく、コストを抑えつつ、管理しやすく、労力をかけずにに運用できるようになること。

CMS

Contents Management Systemの略。概要説明 CMSにはヘッドレスCMSやWordpress、自前のCMSなど様々なCMSがある。要件次第で何を使っても良いが、今回の記事ではヘッドレスで使うことを想定している。これは後述の仕組みを活用するためにAPIでデータを取得・更新できることが必須であるため。またページ公開などをトリガーとして他のシステムと連携するために、Webhookにも対応しているのが望ましい。 後述の理由でマークダウン形式でコンテンツを管理できるものが理想的。

マークダウンでのコンテンツ作成

公開するページの内容をマークダウンで作成するとHTMLやその他独自フォーマットで作成するよりも色々都合が良いという話。

文書構造とデザインの分離

HTMLと比較してマークダウンを使うことの一番の利点は文書構造とデザインを強制的に分離できることが挙げられる。 HTMLの場合、次のように構造とデザインを同時に記述することができる。

<div>
  <h1 style="font-size: 32px; font-weight: bold;">ウェブサイトのメンテナンスについて</h1>
  <h2 style="font-size: 28px; font-weight: bold;">影響範囲と期間</h2>
  <p>
    メンテナンス期間中は
    <strong style="background-color: yellow;">一部のサービスが利用できません</strong>
    のでご注意ください。
  </p>
</div>

一方でマークダウンで記述すると次のようになる。

# ウェブサイトのメンテナンスについて
## 影響範囲と期間

メンテナンス期間中は **一部のサービスが利用できません** のでご注意ください。

ここにはデザインを設定できず、あくまでH1, H2, p, 強調(

**
)など文書の構造を示すだけである。 ではデザインはどうするかというと、別途プログラムでその構造に対応するものを記述する。

function Heading1({ children }) {
  return <h1 style={{ fontSize: '32px', fontWeight: 'bold' }}>{children}</h1>;
}

function Heading2({ children }) {
  return <h2 style={{ fontSize: '28px', fontWeight: 'bold' }}>{children}</h2>;
}

function Strong({ children }) {
  return <strong style={{ backgroundColor: 'yellow' }}>{children}</strong>;
}

// MarkdownのASTで # → Heading1, ## → Heading2, **...** → Strong にマッピング

このように文書構造とデザインを分離することにより、次のようなメリットがある。

デザインの統一・アクセシビリティ対応

マークダウンにはデザインを記述できず、プログラム側でデザインを作り込んでいくことになる。 したがってコンテンツ作成者(非開発者)は用意されたデザインの一覧から使いたいものを選ぶことになる。つまりコンテンツ作成者が独自にデザインを作成することが仕組み的に不可能になる。 全てのデザインは事前に用意されたもののみで構成されるので、必然的にウェブサイト全体のデザインが統一される。これはウェブサイトのアクセシビリティの観点からも対応漏れが生じることを防止する。 また、仮にデザインの崩れが生じた場合にも、責任分界点が明確である。コンテンツ作成者が任意にデザインを設定できると、デザインの崩れがプログラムに起因するのか、コンテンツに起因するのか切り分けが必要になる。一方でデザインがコンテンツから分離されていると、その責任はプログラムのみに起因し、責任の所在が自明であるため、切り分けが不要になる。

移植性

マークダウンではdivなどを記述する必要がない。 HTMLではデザインを適用させるためにdivを用意し、それにクラスを設定して使うことが多々ある。しかしこの構造はそのウェブサイト独自の構造であり、他サイトでは異なる構造を取ることもある。 従って移植性を高く保つためにはデザイン要素(=div)を排除して、どのプラットフォームでもそのまま利用できるようにしておくべきである。 これをしておくことで、複数のウェブサイトを統合する、もしくはその逆の際に、コンテンツの修正作業が発生しない、またはプログラムによる一括処理で書き換えが可能になる。 他にも例えば同じ記事を自社の複数のウェブサイトに掲載する際にも各プラットフォームへのローカライズが不要になる。そもそもCMSを統一できるので、広報戦略への準拠などを監視しやすくなる。CMSを複数持たずに済むので開発コストも下がる。

セキュリティ

マークダウンではHTMLのようにScriptタグでプログラムを記述することができない。 そのため、コンテンツ作成者が、意図せず(もしくは故意に)脆弱性を作り込むことを未然に防ぐことができる。

課題点

埋め込み、iframeなど

基本的にマークダウンの記法に従うため、HTMLのようなタグによる記述はそのままテキストとして扱う。 しかし、例えばYouTubeの動画やGoogleMapなどをウェブサイト上で表示するにはiframeタグが必要になる。 このiframeは任意のURLのウェブサイトを表示できるため、コンテンツ作成者が勝手に関係の無いサイトや悪意あるサイトを表示させることができてしまう。 対策としては3つ。

  1. iframeタグが文書内に存在する場合、それをそのままDOMとして表示する。
  2. マークダウンの拡張記法でiframeに対応するルールを用意し、対応するiframe用のReactコンポーネントでレンダリングする。
  3. YouTubeやGoogleMapなど、埋め込みを許可するものに限り、独自に拡張記法を用意し、それぞれに対応するReactコンポーネントを作成しレンダリングする。 1はコンテンツ作成者がYouTubeなどから埋め込み用のタグをコピーして貼り付けるだけで済むため、記事の作成が簡単である一方、HTMLのタグをそのまま表示できる手立てを残す事になる。検証無しにタグをHTMLとして表示するのはXSSなど攻撃の糸口になりうるので避けられると無難。 2は一応プログラムで変換するため1よりもセキュリティが向上する。ただしコンテンツ作成者が勝手に関係ないサイトを埋め込む場合が考えられるため、変換時にコントロールが必要。特定の属性を付与する必要も考えられ、それを一つのコンポーネントで対応するとなると、場合によっては仕様が複雑化する恐れもある。 3は一番安全だが、埋め込む要素の仕様変更への対応や、各コンポーネントの作成に工数を要する。 以上の手段から要求によってどれを選択するか検討が必要。

Reactコンポーネントを作るのが割に合わないケース

当然コンポーネントを作成するには工数がかかる。ページ数が少なく、コンポーネントの種類が多くなるようなウェブサイトではHTMLを直接書いた方がコスト的にメリットが大きい場合も考えられる。

作成から公開まで

多言語対応

AIにリクエストを投げて翻訳させてもいいかも。チェックは必要だけど。 同一コンテンツについては、slugを確保してURLが同一のコンテンツを指し示すようにするべきかと。/en/を除いたURLが同じだけど、別の内容の記事ってのは避けた方がよいかと。

AIによるチェック

文体、誤字脱字、用語・表記の統一 ※プロンプトなどを任意で設定して基盤モデルにリクエストを送れる環境が用意されている前提。他用途でも使えるからこれ専用ではなくて用意されているのが理想的かと。(情報流出を防ぐためRAG環境とは分離されている方がいいかも?)

人間によるチェック

AIのチェックで怒られた場合に人の承認でGoとするか。

テスト自動化の流用

UIの崩れが無いか、開発側のテスト自動化でのUIのテストを流用できるかも?コンポーネント使っているから崩れないはずだけど、、、。でも初回のテストだと差分も取りようがないから結局使えないかも。

ナビゲーション

設置場所とURL

原則、ヘッダーやサイドのナビゲーションバーの階層構造と同じ形のURL。 複数のナビゲーションバーのリンク元にはエイリアスを置くイメージ。

URLの更新とリダイレクト設定

URLが階層構造に従うとすると、設置場所を変えたときにURLが変わってしまう。原則一度公開したURLは変えないため、変更元のURLはコンテンツのメタデータなどに記録が残るようにしておいて、自動的にリダイレクトが設定されるようにする。新規ページが過去のURLと重複するような場合の運用は要検討(新しいのが奪う感じでいいと思うけど) リダイレクトがループしないようにチェックも必須。 あと、そのためには記事にはUUIDのようなURLやタイトルなどとは独立したユニークなIDを振るべきかと。

ABテスト

考え中。。。 対象ページのURLは、テスト中の被リンクの可能性を考慮すると、同一である方がいいかも。ただ、設計が複雑になるから考え物。

運用とガバナンス

1ユーザ1アカウント

CMSにSSOできるのが理想的。SCIMができてもいい。所属部署に応じた適切な権限設定。

定期的な更新・非公開化の確認

公開日から確認の実施日までの経過時間で更新をサジェスト可能。 終了したイベントの日程などは、AIに読ませることで、明示的にタグ付けされていない文中であっても識別可能。

バージョン管理とバックアップ

「公開」する際のwebhookなどをトリガーとして、S3などに保存。担当者にはReadOnlyで権限を付けて、確認を許可することも可能。誤削除に備えたバックアップや証跡としても機能。

最後に

最初から全部やる必要は全然なく、基本的にはSaaSのCMSの機能だけで十分かと。追加で機能をlambdaで作ったり、ほかのSaaSを入れたりしてAPIで連携できれば御の字。